package edu.tsp.ga;

import edu.tsp.ga.crossover.Crossover;
import edu.tsp.ga.mutators.Mutator;
import edu.tsp.ga.selectors.Selector;
import edu.tsp.ga.utils.Pair;
import edu.tsp.ga.utils.RandomGenerator;

public class Genotype {

    private Population population;
    private Mutator mutator;
    private Crossover crossover;
    private FitnessFunction fitnessFunction;
    private double crossProb;
    private double mutationProb;
    private Selector selector;

    public Genotype(Population population, FitnessFunction fitnessFunction,
                    Mutator mutator, double mutationProb,
                    Crossover crossover, double crossProb, Selector selector) {
        this.mutationProb = mutationProb;
        this.crossProb = crossProb;
        this.population = population;
        this.mutator = mutator;
        this.crossover = crossover;
        this.fitnessFunction = fitnessFunction;
        this.selector = selector;
    }

    public void evolve() {
        Population newPopulation = new Population();
        while (newPopulation.getPopulationSize() < population.getPopulationSize()) {
            double prob = RandomGenerator.nextDouble();
            if(prob <= mutationProb) {
                Chromosome parent = selector.select(population);
                Chromosome chromosome = mutator.mutate(parent);
                newPopulation.addChromosome(chromosome);
            }
            if(prob <= crossProb) {
                Chromosome parent1 = selector.select(population);
                Chromosome parent2 = selector.select(population);
                Pair<Chromosome, Chromosome> pair = crossover.crossover(parent1,parent2);
                newPopulation.addChromosome(pair.head);
                newPopulation.addChromosome(pair.tail);
            }
        }
        this.population = newPopulation;
    }

    public Chromosome getBestChromosome() {
        Chromosome bestChromosome = null;
        double value = Double.MAX_VALUE;
        for(Chromosome chromosome : population.getChromosomes()) {
            double newValue = fitnessFunction.eval(chromosome);
            if(newValue < value) {
                value = newValue;
                bestChromosome = chromosome;
            }
        }
        return bestChromosome;
    }

}
